﻿using System;
using System.Collections.Generic;
using System.Reflection;
using Ex05.logics.utility;

namespace BullsEyeGame
{
    /// <summary>
    /// This class will hold a Guess
    /// </summary>
    internal class Guess
    {
        /// <summary>
        /// An enum to hold all valid chars for the game
        /// </summary>
        public enum eGuessLetters
        {
            [StringValue("A")]
            A,
            [StringValue("B")]
            B,
            [StringValue("C")]
            C,
            [StringValue("D")]
            D,
            [StringValue("E")]
            E,
            [StringValue("F")]
            F,
            [StringValue("G")]
            G,
            [StringValue("H")]
            H
        }              

        private static readonly int sr_NumberOfGuessLetters = 8;
        private const int k_GuessLength = 4;
        private const int k_LetterNotFound = -1;
        
        private readonly List<eGuessLetters> m_GuessLetters = null;        

        /// <summary>
        /// A static constructor
        /// </summary>
        static Guess()
        {
            sr_NumberOfGuessLetters = calculateNumberOfGuessLetters();
        }              

        /// <summary>
        /// Will allow us to get the string property of the letters enum
        /// </summary>
        /// <param name="i_EnumValidChar">an enum to get its string value</param>
        /// <returns>string value</returns>
        public static string GetStringValueForEnumGuessLetter(eGuessLetters i_EnumValidChar)
        {
            const bool v_Inherit = true;
            Type type;
            FieldInfo fieldInfo;
            StringValue[] attrArray;
            string stringValueOfEnum = null;

            type = i_EnumValidChar.GetType();
            fieldInfo = type.GetField(i_EnumValidChar.ToString());
            attrArray = fieldInfo.GetCustomAttributes(typeof(StringValue), !v_Inherit) as StringValue[];

            foreach (StringValue attr in attrArray)
            {
                stringValueOfEnum = attr.Value;
            }

            return stringValueOfEnum;
        }
      
        /// <summary>
        /// A constructor for a Guess from an input of eGuessLetters
        /// </summary>
        /// <param name="i_NewGuessLetters">
        /// A list of the eGuessLetters for the new Guess
        /// <remarks>
        /// If the input is null will throw ArgumentNullException
        /// If the the number of eGuessLetters is smaller then GuessLength
        /// then null will be returned
        /// </remarks>
        /// </param>
        /// <returns>The new Guess</returns>
        public static Guess CreateGuess(List<eGuessLetters> i_NewGuessLetters)
        {
            if (i_NewGuessLetters == null)
            {
                throw new ArgumentNullException("Guess can't be null");
            }

            Guess newGuess = null;
            if (i_NewGuessLetters.Count == GuessLength)
            {
                newGuess = new Guess(i_NewGuessLetters);
            }

            return newGuess;
        }       

        /// <summary>
        /// A property to get the constant Guess length
        /// </summary>
        public static int GuessLength
        {
            get { return k_GuessLength; }
        }

        /// <summary>
        /// A property constant int for a letter that was not found
        /// </summary>
        public static int LetterNotFound
        {
            get { return k_LetterNotFound; }
        }

        /// <summary>
        /// The number of valid eGuessLetters
        /// </summary>
        public static int NumberOfGuessLetters
        {
            get { return sr_NumberOfGuessLetters; }
        }

        /// <summary>
        /// Will return this Guess letter at location index
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public eGuessLetters At(int index)
        {
            return m_GuessLetters[index];
        }

        /// <summary>
        /// Will return all this Guess eGuessLetters
        /// </summary>
        public List<eGuessLetters> GuessLetters
        {
            get { return m_GuessLetters; }
        }

        /// <summary>
        /// Will return the index of the input in this Guess letters 
        /// </summary>
        /// <param name="i_GuessLetterToFind">The letter to find</param>
        /// <param name="i_StartSearchIndex">
        /// The index to start the search at
        /// <remarks>
        /// This value must be in valid range ie. grater or equal to zero and smaller then
        /// current number of letters in this Guess else it will throw IndexOutOfRangeException
        /// </remarks>
        /// </param>
        /// <returns></returns>
        public int IndexOf(eGuessLetters i_GuessLetterToFind, int i_StartSearchIndex)
        {            
            if (i_StartSearchIndex < 0 || i_StartSearchIndex > m_GuessLetters.Count)
            { 
                throw new IndexOutOfRangeException("can't start search out of the bound of the array");
            }

            return findGuessLetterIndexInGuessList(i_StartSearchIndex, i_GuessLetterToFind);
        }

        private Guess(List<eGuessLetters> i_GuessLetters)
        {
            m_GuessLetters = i_GuessLetters;
        }

        private static int calculateNumberOfGuessLetters()
        {
            Array guessLetters = System.Enum.GetValues(typeof(eGuessLetters));
            return guessLetters.Length;
        }

        private int findGuessLetterIndexInGuessList(int i_StartIndex, eGuessLetters i_GuessLetterToFind)
        {
            int result = LetterNotFound;
            bool foundMatch = false; 
            eGuessLetters currentGuess;

            for (int i = i_StartIndex; i < m_GuessLetters.Count && !foundMatch; ++i)
            {
                currentGuess = m_GuessLetters[i];
                if (i_GuessLetterToFind == currentGuess)
                {
                    foundMatch = true;
                    result = i;
                }
            }

            return result;
        }
    }
}
